----------------------------------------------------------------------------------
-- Company: 
-- Engineer: 
-- 
-- Create Date:    08:43:25 11/05/2012 
-- Design Name: 
-- Module Name:    Customized_Interrupt_Handler - Interrupt_Controller 
-- Project Name: 
-- Target Devices: 
-- Tool versions: 
-- Description: 
--
-- Dependencies: 
--
-- Revision: 
-- Revision 0.01 - File Created
-- Additional Comments: 
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
--use IEEE.NUMERIC_STD.ALL;

-- Uncomment the following library declaration if instantiating
-- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity Customized_Interrupt_Handler is
    Port ( clk : in  STD_LOGIC;
           rst : in  STD_LOGIC;
           CLEAR_CURR_INTERRUPT : in  STD_LOGIC;
           FLAG_CHANGED : out  STD_LOGIC;
           ACK_CHANGED : in  STD_LOGIC;
           CURR_INT : in  STD_LOGIC_VECTOR (3 downto 0);
           NEXT_INT : out  STD_LOGIC_VECTOR (3 downto 0);
           IE_reg : in  STD_LOGIC_VECTOR (7 downto 0);
           IP_reg : in  STD_LOGIC_VECTOR (7 downto 0);
           TCON_reg : in  STD_LOGIC_VECTOR (7 downto 0);
           OUT_TCON_reg : out  STD_LOGIC_VECTOR (7 downto 0);
           INT0 : in  STD_LOGIC;
           INT1 : in  STD_LOGIC;
           RI : in  STD_LOGIC;
           TI : in  STD_LOGIC);
end Customized_Interrupt_Handler;

architecture Interrupt_Controller of Customized_Interrupt_Handler is

signal INT0_PREV: STD_LOGIC;
signal INT1_PREV: STD_LOGIC;
begin
	process(RST, CLK, CLEAR_CURR_INTERRUPT)
	variable INT0_TRIGGER: STD_LOGIC;
	variable INT1_TRIGGER: STD_LOGIC;
	variable Updated		: STD_LOGIC;
	begin
		if( RST = '1' ) then
			NEXT_INT <= "0000";
			OUT_TCON_reg <= "00000000";
			INT0_PREV <= '0';
			INT1_PREV <= '0';
			INT0_TRIGGER := '0';
			INT1_TRIGGER := '0';
			Updated := '1';
			FLAG_CHANGED <= '0';
		--================= CLEAR SERVING INTERRUPT FLAG==================
		elsif( CLEAR_CURR_INTERRUPT = '1' ) then
			case CURR_INT(2 Downto 0) is
				when "001"	=>
					-- Serial Interrupt will not be cleared by hardware
					OUT_TCON_reg <= TCON_reg;
				when "010"	=>
					-- Reset Timer1 Overflow Interrupt
					--OUT_TCON_reg <= '0' & TCON_reg(6 Downto 0);
					OUT_TCON_reg <= TCON_reg AND "01111111";
				when "011"	=>
					-- Reset External Interrupt 1
					if( TCON_reg(2) = '1' ) then
						--OUT_TCON_reg <= TCON_reg(7 Downto 4) & '0' & TCON_reg(2 Downto 0);
						INT1_TRIGGER := '0';
						OUT_TCON_reg <= TCON_reg AND "11110111";
					else
						-- Level Trigger will not be reset
						OUT_TCON_reg <= TCON_reg;
					end if;
				when "100"	=>
					-- Reset Timer0 Overflow Interrupt
					--OUT_TCON_reg <= TCON_reg(7 Downto 6) & '0' & TCON_reg(4 Downto 0);
					OUT_TCON_reg <= TCON_reg AND "11011111";
				when "101"	=>
					-- Reset External Interrupt 0
					if( TCON_reg(0) = '1' ) then
						--OUT_TCON_reg <= TCON_reg(7 Downto 2) & '0' & TCON_reg(0);
						INT0_TRIGGER := '0';
						OUT_TCON_reg <= TCON_reg AND "11111101";
					else
						-- Level Trigger will not be reset
						OUT_TCON_reg <= TCON_reg;
					end if;
				when others =>
					OUT_TCON_reg <= TCON_reg;
			end case;
			
			Updated := '0';
			FLAG_CHANGED <= '1';
		elsif(clk'event and clk ='1') then
			--===================== Check for Acknowledgement =====================
			if (ACK_CHANGED = '1') then
				Updated := '1';
				FLAG_CHANGED <= '0';
			end if;
		
			-- If the TCON is synchronized with reg file. Continou checking
			if(Updated='1') then
				--================= NEXT INTERRUPT DETERMINATION==================
				if(IE_reg(7) = '0') then
					NEXT_INT <= "0000";
					-- INTERRUPT PRIORITY is set
				elsif( IP_reg(0) ='1' and IE_reg(0) = '1' and TCON_reg(1) = '1') then
					NEXT_INT <= "1101";
				elsif( IP_reg(1) ='1' and IE_reg(1) = '1' and TCON_reg(5) = '1') then
					NEXT_INT <= "1100";
				elsif( IP_reg(2) ='1' and IE_reg(2) = '1' and TCON_reg(3) = '1') then
					NEXT_INT <= "1011";
				elsif( IP_reg(3) ='1' and IE_reg(3) = '1' and TCON_reg(7) = '1') then
					NEXT_INT <= "1010";
				elsif( IP_reg(4) ='1' and IE_reg(4) = '1' and (RI = '1' or TI = '1')) then
					NEXT_INT <= "1001";
					
				elsif(IP_reg(0) ='0' and IE_reg(0) = '1' and TCON_reg(1) = '1') then
					NEXT_INT <= "0101";
				elsif(IP_reg(0) ='0' and IE_reg(1) = '1' and TCON_reg(5) = '1') then
					NEXT_INT <= "0100";
				elsif(IP_reg(0) ='0' and IE_reg(2) = '1' and TCON_reg(3) = '1') then
					NEXT_INT <= "0011";
				elsif(IP_reg(0) ='0' and IE_reg(3) = '1' and TCON_reg(7) = '1') then
					NEXT_INT <= "0010";
				elsif(IP_reg(0) ='0' and IE_reg(4) = '1' and (RI = '1' or TI = '1')) then
					NEXT_INT <= "0001";
				else
					NEXT_INT <= "0000";
				end if;
				--=================================================================
				
				--================== EXTERNAL INTERRUPT FLAG UPDATING =============
				-- INT 0 Flag checking
				if( IE_reg(0) = '1') then
					if( TCON_reg(0) = '1' ) then		-- Edge trigger
						if(INT0_PREV = '1' and INT0 = '0') then
							INT0_TRIGGER := '1';
						end if;
					elsif( TCON_reg(0) = '0' ) then	-- Level trigger
						if(INT0 = '0') then
							INT0_TRIGGER := '1';
						else
							INT0_TRIGGER := '0';
						end if;
					end if;
				else
					INT0_TRIGGER := TCON_reg(1);
				end if;
				
				-- INT 1 Flag checking
				if(IE_reg(2) = '1') then
					if(TCON_reg(2) = '1') then		-- Edge trigger
						if(INT1_PREV = '1' and INT1 = '0') then
							INT1_TRIGGER := '1';
						end if;
					elsif(TCON_reg(2) = '0') then	-- Level trigger
						if(INT1 = '0') then
							INT1_TRIGGER := '1';
						else
							INT1_TRIGGER := '0';
						end if;
					end if;
				else
					INT1_TRIGGER := TCON_reg(3);
				end if;
				
				-- check Whether need to update the TCON
				if( INT0_TRIGGER /= TCON_reg(1) OR INT1_TRIGGER /= TCON_reg(3) ) then
					OUT_TCON_reg <= TCON_reg(7 Downto 4) & INT1_TRIGGER & TCON_reg(2) & INT0_TRIGGER & TCON_reg(0);
					Updated := '0';
					FLAG_CHANGED <= '1';
				else
					OUT_TCON_reg <= TCON_reg;
				end if;
				
				--=================================================================
			end if;
			INT0_PREV <= INT0;
			INT1_PREV <= INT1;
		end if;
	end process;

end Interrupt_Controller;

